You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Put the gist of the writing instructions in every agent session - #1014
The plugin's SessionStart hook (#1009) tells every session to run basecamp instructions before writing to Basecamp, and carries nothing else. On one machine, of 19 agent sessions that had that pointer and then posted to Basecamp, 8 never ran the command, 7 ran it through head -5 or tail, and 4 read it in full. The command printed its precedence preface first, four lines long, so head -5 showed the frame around the rules and none of the rules. Putting the full text in the system prompt was tried elsewhere and did not help either.
basecamp instructions --brief prints each source (baseline, then profile) as its Source line, then one line per top-level list item: the bold text the item starts with, or else its first sentence. Headings, prose, code blocks and nested items are left out. JSON output carries the same lines as brief on each document, next to content.
The full output leads with the rules. Each source prints first (Source line, then text). The preface comes last, shortened to what it has to say: the sources are the operator's defaults and never come from Basecamp; the later source wins; a project's AGENTS.md wins on how posts look, the person on voice, and a single-post instruction over all of these; none of it overrides the skill.
The hook carries the gist. After the existing pointer it appends The gist (run the command for the full text): and the brief lines, but only when every source of the active profile can be read without the network: a file, or a URL's cached copy of any age. It never fetches, because the hook has a 5-second budget. With no cached copy yet, or no list to cut down, the pointer goes alone as before. The hook's app doesn't scope the cache dir by profile the way root does, so the gist looks for the cache under profiles/<name>. The hook's own state stays where it was.
For the coworker style guide (11 bold-led rules) the hook's whole context is 963 characters: the pointer plus 11 lines, about 85 words of rules.
Verification
Unit tests for the brief: bold lead, __ bold, bold wrapping a line, no bold (first sentence), bullets of every kind, 1) numbering, nested items, headings and prose, code fences, thematic breaks, no list, CRLF, terminal controls, clipping.
Output order: rules before the preface, baseline before profile, --brief text output, and brief in JSON.
Hook: a stale cached URL gives the gist, and the whole notice stays under 1,500 characters. No cache gives the pointer alone, and so does one of two layers uncached. Two file layers are labelled. A source with no list gives no gist. A profile-scoped cache is found. Every hook test swaps in an HTTP client and gh stand-in that fail the test if the hook fetches. With the gist disabled, the gist tests fail. With the profile scoping removed, the profile-cache test fails.
I ran the built binary against this machine's real config. --brief and the hook both print the 11 rules.
Local: go vet, gofmt, golangci-lint run ./internal/commands/, go test ./internal/commands/, make check-surface check-skill-drift test-sync-skills all pass. .surface was regenerated for --brief. The full bin/ci (e2e and the other gates) did not run locally because the machine's shared build lock was held, so this PR's CI covers the rest.
Alternatives
Full text in the hook: too long for every session's context. It also needs a fetch the hook can't wait for, and the full text in the system prompt didn't help elsewhere.
Fetching in the hook when there's no cache: risks the 5-second budget. The first basecamp instructions run fills the cache, and later sessions get the gist.
A brief maintained by hand in the source doc (front matter, say): exact, but it drifts from the rules. Deriving the brief from bold leads needs nothing from the doc's authors beyond the shape it already has.
Brief first, then the full text, in the plain command: helps head readers further but duplicates every rule. Left to --brief.
The SessionStart hook told each session to run `basecamp instructions`
before writing to Basecamp, and carried nothing else. Sessions skipped it,
or read it through `head -5` or `tail`; the command printed its precedence
preface first, four lines long, so a session that read only the top read
the frame and none of the rules.
Now:
- `basecamp instructions --brief` prints each source as one line per
top-level list item: the bold text the item starts with, or else its
first sentence. Headings, prose, code blocks and nested items are left
out. The JSON output carries the same lines as `brief` on each document,
next to the full `content`.
- `basecamp instructions` prints each source first (its Source line, then
its text) and the preface last, shortened to what it has to say: the
sources are the operator's defaults, never from Basecamp; the later
source wins; a project's AGENTS.md wins on how posts look, the person on
voice, and a single-post instruction over all of these; none of it
overrides the skill.
- The hook appends "The gist (run the command for the full text):" and the
brief lines when every source of the active profile can be read without
the network: a file, or a URL's cached copy, however old. It never
fetches. With no cached copy yet, or no list to cut down, the pointer
goes alone as before. It looks for the cache under profiles/<name>, as
the command keeps it, without moving the hook's own state.
For a style guide of eleven bold-led rules the whole hook context is about
960 characters: the pointer and eleven lines, about 85 words of rules.
Verified with unit tests for the brief (bold lead, no bold, bullets,
nested items, headings, code blocks, thematic breaks, CRLF, control
characters, clipping), for the output order and --brief, for the JSON
brief, and for the hook with a stale cache, without a cache, with one of
two layers uncached, with two file layers, with no list, and with a
profile-scoped cache, each using an HTTP client and gh stand-in that fail
the test if the hook fetches. The gist tests were run against the code
with the gist disabled and fail there. go vet, gofmt, golangci-lint on
internal/commands, the internal/commands tests, check-surface,
check-skill-drift and test-sync-skills pass locally; the full bin/ci did
not run here (the shared build lock was held), so remote CI covers the
rest.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The session-start gist lacks an aggregate size limit and can exceed the 10,000-character context budget, causing later rules—including the higher-precedence profile layer—to be lost.
This PR builds on the SessionStart instructions pointer by adding a locally available gist, helping agents see writing rules without fetching them during startup.
Changes:
Adds basecamp instructions --brief and a brief field in JSON output.
Moves full instructions before the precedence preface.
Adds file- or cache-backed summaries to SessionStart, with profile-aware cache lookup and supporting tests.
[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
File
Description
skills/basecamp-connect/SKILL.md
Documents the SessionStart gist.
README.md
Updates Codex session-context guidance.
internal/commands/instructions.go
Adds brief output, cache-only loading, and rules-first rendering.
internal/commands/instructions_test.go
Tests output ordering and brief rendering.
internal/commands/instructions_brief.go
Extracts concise leads from top-level Markdown list items.
internal/commands/instructions_brief_test.go
Tests extraction, sanitization, clipping, and JSON output.
internal/commands/agent_hook_instructions.go
Adds offline gist generation and profile-aware cache lookup.
…own does
Review findings on the brief: a gist past the hook's context budget, or one
missing a source with no list, now falls back to the pointer alone; a fence
closes only on a run of its own character at least as long; an indented
heading or break inside an item keeps it open; #tag is text, not a heading.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Markdown fence handling misclassifies indented examples and list-item fences, causing --brief and the SessionStart gist to include example code or omit real instructions.
No open issues 🎉
2 previously missed issues
In code that hasn't changed since last review
Ignore fence-like backticks in indented code examples
internal/commands/instructions_brief.go:70
Matching fences against trimmed treats literal backticks in a four-space-indented code example as a fence opener, so subsequent real rules disappear from --brief and the SessionStart gist. Recognize fences only at valid indentation relative to their Markdown container, leaving indented code examples out. Add a regression case with rules before and after an indented example containing an unmatched literal fence.
Recognize fences immediately after list markers
internal/commands/instructions_brief.go:105
When a list item starts with a code fence immediately after its marker, this assignment collects the fence as rule text; the closing fence is then mistaken for an opener, swallowing subsequent rules. Both --brief and the SessionStart gist can therefore include example code while omitting real instructions. Recognize fence openers in marker-stripped list content, including nested items, and add a regression test that preserves the following real rule.
… too
Copilot's second round on #1014: a fence indented four past its container is
text in an indented code example, and an item, or a nested one, that opens
with a fence starts a code block rather than a lead.
🤖 Copilot's review of c104e25 listed two previously missed issues in its body, both fixed in 4a851bd:
Fence-like backticks in indented code examples. A fence now opens a block only at an indentation its container allows (up to three spaces past the list item's content, or the margin), so a four-space-indented example is text. Regression case: rules before and after an indented example holding an unmatched fence.
Fences right after a list marker. An item, or a nested item, whose content opens with a fence now starts the code block instead of collecting the fence as its lead. Regression cases for both keep the following real rule.
🤖 Copilot's review of 5203be1 has no open issues; its overview asks for a human look at fence boundaries and HTML comments. The brief is a heuristic summary, and every place it appears (`--brief` and the session gist) sends the reader to `basecamp instructions` for the full text, so a missed edge case costs a line of gist, never the rules themselves. The fence cases it raised in earlier rounds are covered by regression tests (nested fences, a shorter fence inside a longer one, a fence after a list marker, indented code). An HTML comment holding a list is left as a known limit.
…950575
* origin/main:
Fix show lookups for supported Basecamp item URLs (#1017)
Stamp v0.25.0 (#1016)
Put the gist of the writing instructions in every agent session (#1014)
Stamp v0.24.3 (#1013)
Read a slow keyring instead of calling its credential missing (#1011)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
The plugin's SessionStart hook (#1009) tells every session to run
basecamp instructionsbefore writing to Basecamp, and carries nothing else. On one machine, of 19 agent sessions that had that pointer and then posted to Basecamp, 8 never ran the command, 7 ran it throughhead -5ortail, and 4 read it in full. The command printed its precedence preface first, four lines long, sohead -5showed the frame around the rules and none of the rules. Putting the full text in the system prompt was tried elsewhere and did not help either.Tracked on the Kitchen card.
Change
basecamp instructions --briefprints each source (baseline, then profile) as its Source line, then one line per top-level list item: the bold text the item starts with, or else its first sentence. Headings, prose, code blocks and nested items are left out. JSON output carries the same lines asbriefon each document, next tocontent.The gist (run the command for the full text):and the brief lines, but only when every source of the active profile can be read without the network: a file, or a URL's cached copy of any age. It never fetches, because the hook has a 5-second budget. With no cached copy yet, or no list to cut down, the pointer goes alone as before. The hook's app doesn't scope the cache dir by profile the way root does, so the gist looks for the cache underprofiles/<name>. The hook's own state stays where it was.For the coworker style guide (11 bold-led rules) the hook's whole context is 963 characters: the pointer plus 11 lines, about 85 words of rules.
Verification
__bold, bold wrapping a line, no bold (first sentence), bullets of every kind,1)numbering, nested items, headings and prose, code fences, thematic breaks, no list, CRLF, terminal controls, clipping.--brieftext output, andbriefin JSON.--briefand the hook both print the 11 rules.go vet,gofmt,golangci-lint run ./internal/commands/,go test ./internal/commands/,make check-surface check-skill-drift test-sync-skillsall pass..surfacewas regenerated for--brief. The fullbin/ci(e2e and the other gates) did not run locally because the machine's shared build lock was held, so this PR's CI covers the rest.Alternatives
basecamp instructionsrun fills the cache, and later sessions get the gist.headreaders further but duplicates every rule. Left to--brief.